import cv2
import numpy as np
import torch
import onnxruntime
from typing import Union, List, Tuple, Optional
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import time
import torchvision
import sys
import json
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QFileDialog, QMessageBox
from PyQt5.QtGui import QPixmap, QImage, QKeySequence
from PyQt5.QtCore import Qt
from main_ui import Ui_MainWindow
from utils import letterbox, non_max_suppression, scale_coords
from my_logger import *
from yolov5_detect import Yolov5SCatDetector, Yolov5SCatFaceDetector
from efficientNet import EfficientNetV2B0_128_ONNX
import os, sys
from PyQt5.QtWidgets import QMainWindow  # 添加这行导入
from PyQt5.QtWidgets import QSizePolicy
from PyQt5.QtCore import QTimer
import signal


class MyWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)  # 加载UI布局

        # 添加视频控制相关变量
        self.video_capture = None
        self.video_timer = QTimer()
        self.video_timer.timeout.connect(self.run_video)
        self.is_video_playing = False
        self.current_frame_pos = 0
        self.interval = None
        self.fps = None

        # 添加USB相机相关变量
        self.usb_camera = None
        self.usb_camera_timer = QTimer()
        self.usb_camera_timer.timeout.connect(self.run_usb_camera)
        self.is_usb_camera_running = False

        # 可配置参数
        self.detection_window_size = 60  # 离开检测窗口大小
        self.leave_threshold = 60       # 离开判断阈值
        self.appearance_window = 12     # 出现检测窗口大小
        self.appearance_threshold = 6   # 出现判断阈值
        
        self.recognition_window_size = 12  # 识别窗口大小(帧数)
        self.recognition_threshold = 6    # 识别阈值(需要多少帧匹配)
        # # 设置窗口固定尺寸为1200x800像素
        

        # self.img_show_label.setFixedSize(850, 430)  # 设置一个略小于窗口的固定尺寸
        # self.img_show_label.setAlignment(QtCore.Qt.AlignCenter)
        # self.img_show_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        # self.label_register1.setFixedSize(439, 220)  # 设置一个略小于窗口的固定尺寸
        # self.label_register1.setAlignment(QtCore.Qt.AlignCenter)
        # self.label_register1.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        # self.label_register2.setFixedSize(439, 220)  # 设置一个略小于窗口的固定尺寸
        # self.label_register2.setAlignment(QtCore.Qt.AlignCenter)
        # self.label_register2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        # self.label_register3.setFixedSize(439, 220)  # 设置一个略小于窗口的固定尺寸
        # self.label_register3.setAlignment(QtCore.Qt.AlignCenter)
        # self.label_register3.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        # self.label_recog_cat1.setFixedSize(439, 220)  # 设置一个略小于窗口的固定尺寸
        # self.label_recog_cat1.setAlignment(QtCore.Qt.AlignCenter)
        # self.label_recog_cat1.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        # self.label_recog_cat2.setFixedSize(439, 220)  # 设置一个略小于窗口的固定尺寸
        # self.label_recog_cat2.setAlignment(QtCore.Qt.AlignCenter)
        # self.label_recog_cat2.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        # 设置窗口固定尺寸为1400x1000像素
        self.setFixedSize(1400, 1000)
        # self.setFixedSize(2800, 2000)

        # 计算 img_show_label 的尺寸（60%宽度，43%高度）
        img_width = int(self.width() * 0.6)  # 1400 * 0.6 = 840
        img_height = int(self.height() * 0.43)  # 1000 * 0.43 = 430
        self.img_show_label.setFixedSize(img_width, img_height)
        self.img_show_label.setAlignment(QtCore.Qt.AlignCenter)

        # 计算 register/recog 标签的尺寸（31.35%宽度，22%高度）
        label_width = int(self.width() * 0.25)  # 1400 * 0.3135 ≈ 439
        label_height = int(self.height() * 0.22)  # 1000 * 0.22 = 220

        labels = [
            self.label_register1, self.label_register2, self.label_register3,
            self.label_recog_cat1, self.label_recog_cat2, self.label_recog_cat3
        ]

        for label in labels:
            label.setFixedSize(label_width, label_height)
            label.setAlignment(QtCore.Qt.AlignCenter)
        
        # 初始化日志系统，传入UI中的QTextEdit控件
        self.logger = setup_logging(self.log_plainTextEdit)
        
        # 设置日志文本框为只读
        self.log_plainTextEdit.setReadOnly(True)
        # 测试日志输出
        self.logger.debug("应用程序启动")
        self.logger.debug("日志系统初始化完成")
        # Initialize detector as None
        self.cat_detector = None
        self.cat_detector = None
        self.face_recognizer = None

        self.cat_detector_output_img = []
        self.face_detector_output_img = []

        # 初始化注册图片变量
        self.register_image1 = None
        self.register_image2 = None
        self.register_image3 = None

        self.register_imgpath_list = []
        self.register_embeddings_list = []

            # 添加初始化状态标志
        self.detectors_initialized = False
        self.embeddings_computed = False

        self.camera_img = None
        # 初始化类变量保存UI状态
        self.config_data = {
            'cat_det_enabled': False,
            'face_det_enabled': False,
            'face_recog_enabled': False,
            'bbox_enabled': False,
            'cat_iou_thres': 0.0,
            'cat_conf_thres': 0.0,
            'face_iou_thres': 0.0,
            'face_conf_thres': 0.0,
            'face_dist_thres': 0.0,
            'img_path': '',
            'video_path': '',
            'folder_path': '',
            'cat_det_onnx_path': '',
            'cat_face_det_onnx_path': '',
            'cat_face_recog_onnx_path': ''
        }
        
        # 连接按钮点击事件
        self.btn_selectImg.clicked.connect(self.select_image)
        self.btn_selectVideo.clicked.connect(self.select_video)
        self.btn_selectVideo_2.clicked.connect(self.select_usb_camera)
        self.btn_selectFolder.clicked.connect(self.select_folder)
        self.btn_loadConfig.clicked.connect(self.load_config)
        self.btn_saveConfig.clicked.connect(self.save_config)
        self.btn_catDetection.clicked.connect(self.select_cat_det_onnx)
        self.btn_catfaceDetection.clicked.connect(self.select_cat_face_det_onnx)
        self.btn_catfaceRecog.clicked.connect(self.select_cat_face_recog_onnx)
        self.btn_start.clicked.connect(self.run)
        self.btn_register1.clicked.connect(lambda: self.register_image(1))
        self.btn_register2.clicked.connect(lambda: self.register_image(2))
        self.btn_register3.clicked.connect(lambda: self.register_image(3))
        
        # 连接信号更新类变量
        self.checkBox_catDet.stateChanged.connect(self.update_config_data)
        self.checkBox_faceDect.stateChanged.connect(self.update_config_data)
        self.checkBox_faceRecog.stateChanged.connect(self.update_config_data)
        self.dsb_catDec_iouThres.valueChanged.connect(self.update_config_data)
        self.dsb_catDec_confThres.valueChanged.connect(self.update_config_data)
        self.dsb_faceDec_iouThres.valueChanged.connect(self.update_config_data)
        self.dsb_faceDec_confThres.valueChanged.connect(self.update_config_data)
        self.dsb_facerecog_distThres.valueChanged.connect(self.update_config_data)

        self.load_config(default_config_path="config/usb_default.json")  # 传入默认配置文件路径

    def update_config_data(self):
        """更新类变量中的配置数据"""
        self.config_data['cat_det_enabled'] = self.checkBox_catDet.isChecked()
        self.config_data['face_det_enabled'] = self.checkBox_faceDect.isChecked()  
        self.config_data['face_recog_enabled'] = self.checkBox_faceRecog.isChecked()  
        self.config_data['bbox_enabled'] = self.checkBox_bbox.isChecked()  
        self.config_data['cat_iou_thres'] = self.dsb_catDec_iouThres.value()
        self.config_data['cat_conf_thres'] = self.dsb_catDec_confThres.value()
        self.config_data['face_iou_thres'] = self.dsb_faceDec_iouThres.value()
        self.config_data['face_conf_thres'] = self.dsb_faceDec_confThres.value()
        self.config_data['face_dist_thres'] = self.dsb_facerecog_distThres.value()
        self.config_data['img_path'] = self.path_selectImg.text()
        self.config_data['video_path'] = self.path_selectVideo.text()
        self.config_data['folder_path'] = self.path_selectFolder.text()
        self.config_data['cat_det_onnx_path'] = self.path_catDect.text()
        self.config_data['cat_face_det_onnx_path'] = self.path_catfaceDect.text()
        self.config_data['cat_face_recog_onnx_path'] = self.path_catfaceRecog.text()
        # 新增注册图片路径保存
        self.config_data['register_img_1_path'] = self.register_image1 if hasattr(self, 'register_image1') else ''
        self.config_data['register_img_2_path'] = self.register_image2 if hasattr(self, 'register_image2') else ''
        self.config_data['register_img_3_path'] = self.register_image3 if hasattr(self, 'register_image3') else ''
        # 新增USB相机状态
        self.config_data['usb_camera_enabled'] = self.usb_camera is not None
        self.config_data['is_usb_camera_running'] = self.is_usb_camera_running

    def register_image(self, register_num):
        """注册图片并显示在对应的label中"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, f"选择注册图片 {register_num}", "", 
            "图片文件 (*.png *.jpg *.jpeg *.bmp *.gif)"
        )
        
        if file_path:
            # 保存路径到对应的变量
            if register_num == 1:
                self.register_image1 = file_path
            elif register_num == 2:
                self.register_image2 = file_path
            elif register_num == 3:
                self.register_image3 = file_path
            
            # 显示图片
            label = getattr(self, f'label_register{register_num}')
            pixmap = QPixmap(file_path)
            if not pixmap.isNull():
                scaled_pixmap = pixmap.scaled(
                    label.size(), 
                    Qt.KeepAspectRatio, 
                    Qt.SmoothTransformation
                )
                label.setPixmap(scaled_pixmap)
            
            # 确保列表至少有3个元素
            while len(self.register_imgpath_list) < 3:
                self.register_imgpath_list.append(None)
            
            # 替换对应位置的路径
            self.register_imgpath_list[register_num - 1] = file_path
            
            # 更新配置数据
            self.update_config_data()


    def select_image(self):
        """选择图片并显示（互斥）"""
        self.clear_selections()  # 先清空其他选择
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择图片", "", 
            "图片文件 (*.png *.jpg *.jpeg *.bmp *.gif)"
        )
        label_size = self.img_show_label.size()
        self.logger.debug(f"label size: {label_size.width()}x{label_size.height()}")

        if file_path:
            self.path_selectImg.setText(file_path)
            pixmap = QPixmap(file_path)
            if not pixmap.isNull():
                scaled_pixmap = pixmap.scaled(
                    self.img_show_label.size(), 
                    QtCore.Qt.KeepAspectRatio, 
                    QtCore.Qt.SmoothTransformation
                )
                self.img_show_label.setPixmap(scaled_pixmap)

            self.update_config_data()

    def select_video(self):
        """选择视频文件（互斥）"""
        self.clear_selections()  # 先清空其他选择
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择视频", "", 
            "视频文件 (*.mp4 *.avi *.mov)"  # 这里已经包含.avi格式
        )
        if file_path:
            self.path_selectVideo.setText(file_path)
            self.update_config_data()

    def select_folder(self):
        """选择文件夹（互斥）"""
        self.clear_selections()  # 先清空其他选择
        folder_path = QFileDialog.getExistingDirectory(
            self, "选择文件夹", ""
        )
        if folder_path:
            self.path_selectFolder.setText(folder_path)
            self.update_config_data()

    def select_cat_det_onnx(self):
        """选择猫检测ONNX模型文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择猫检测ONNX模型", "", 
            "ONNX模型文件 (*.onnx)"
        )
        if file_path:
            self.path_catDect.setText(file_path)
            self.update_config_data()

    def select_cat_face_det_onnx(self):
        """选择猫脸检测ONNX模型文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择猫脸检测ONNX模型", "", 
            "ONNX模型文件 (*.onnx)"
        )
        if file_path:
            self.path_catfaceDect.setText(file_path)
            self.update_config_data()

    def select_cat_face_recog_onnx(self):
        """选择猫脸识别ONNX模型文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择猫脸识别ONNX模型", "", 
            "ONNX模型文件 (*.onnx)"
        )
        if file_path:
            self.path_catfaceRecog.setText(file_path)
            self.update_config_data()

    # 在load_config方法中添加USB相机状态的恢复
    def load_config(self, default_config_path=None):
        """加载配置文件，支持手动选择和默认路径"""
        if default_config_path is None:
            # 手动选择模式
            file_path, _ = QFileDialog.getOpenFileName(
                self, "加载配置文件", "", "JSON文件 (*.json)"
            )
        else:
            # 自动加载默认配置
            file_path = default_config_path
    
        if file_path:
            try:
                QApplication.processEvents()
                with open(file_path, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                
                # 更新UI控件状态
                self.checkBox_catDet.setChecked(config.get('cat_det_enabled', False))
                self.checkBox_faceDect.setChecked(config.get('face_det_enabled', False))
                self.checkBox_faceRecog.setChecked(config.get('face_recog_enabled', False))
                self.checkBox_bbox.setChecked(config.get('bbox_enabled', False))
                self.dsb_catDec_iouThres.setValue(config.get('cat_iou_thres', 0.0))
                self.dsb_catDec_confThres.setValue(config.get('cat_conf_thres', 0.0))
                self.dsb_faceDec_iouThres.setValue(config.get('face_iou_thres', 0.0))
                self.dsb_faceDec_confThres.setValue(config.get('face_conf_thres', 0.0))
                self.dsb_facerecog_distThres.setValue(config.get('face_dist_thres', 0.0))
                
                label_size = self.img_show_label.size()
                self.logger.debug(f"label size: {label_size.width()}x{label_size.height()}")

                # 更新路径显示
                img_path = config.get('img_path', '')
                if img_path:
                    self.path_selectImg.setText(img_path)
                    pixmap = QPixmap(img_path)
                    if not pixmap.isNull():
                        scaled_pixmap = pixmap.scaled(
                            self.img_show_label.size(), 
                            Qt.KeepAspectRatio, 
                            Qt.SmoothTransformation
                        )
                        self.img_show_label.setPixmap(scaled_pixmap)
                
                self.path_selectVideo.setText(config.get('video_path', ''))
                self.path_selectFolder.setText(config.get('folder_path', ''))
                self.path_catDect.setText(config.get('cat_det_onnx_path', ''))
                self.path_catfaceDect.setText(config.get('cat_face_det_onnx_path', ''))
                self.path_catfaceRecog.setText(config.get('cat_face_recog_onnx_path', ''))
                
                # 加载注册图片路径并显示
                for i in range(1, 4):
                    img_path = config.get(f'register_img_{i}_path', '')
                    if img_path and os.path.exists(img_path):
                        label = getattr(self, f'label_register{i}')
                        pixmap = QPixmap(img_path)
                        if not pixmap.isNull():
                            scaled_pixmap = pixmap.scaled(
                                label.size(),
                                Qt.KeepAspectRatio,
                                Qt.SmoothTransformation
                            )
                            label.setPixmap(scaled_pixmap)
                        setattr(self, f'register_image{i}', img_path)
                        self.register_imgpath_list.append(img_path)
                
                # 更新类变量
                self.config_data.update(config)
                
                # 恢复USB相机状态
                if config.get('usb_camera_enabled', False):
                    self.select_usb_camera()
                    if config.get('is_usb_camera_running', False):
                        self.start_usb_camera()
                
            except Exception as e:
                QMessageBox.warning(self, "错误", f"加载配置文件失败: {str(e)}")


    def save_config(self):
        """保存配置到JSON文件"""
        self.update_config_data()  # 确保数据是最新的
        
        file_path, _ = QFileDialog.getSaveFileName(
            self, "保存配置文件", "", 
            "JSON文件 (*.json)"
        )
        
        if file_path:
            try:
                if not file_path.endswith('.json'):
                    file_path += '.json'
                
                with open(file_path, 'w', encoding='utf-8') as f:
                    json.dump(self.config_data, f, indent=4)
                
                QMessageBox.information(self, "成功", "配置保存成功！")
                
            except Exception as e:
                QMessageBox.warning(self, "错误", f"保存配置文件失败: {str(e)}")


    def initialize_detectors(self):
        """根据配置初始化检测器"""
        try:
            # 初始化猫检测器
            if self.config_data['cat_det_onnx_path'] and os.path.exists(self.config_data['cat_det_onnx_path']):
                self.logger.debug(f"Initializing cat detector with model: {self.config_data['cat_det_onnx_path']}")
                self.cat_detector = Yolov5SCatDetector(
                    self.config_data['cat_det_onnx_path'],
                    conf_thres=self.config_data['cat_conf_thres'],
                    iou_thres=self.config_data['cat_iou_thres']
                )
                self.logger.debug("Cat detector initialized successfully")
            
            # 初始化猫脸检测器
            if self.config_data['cat_face_det_onnx_path'] and os.path.exists(self.config_data['cat_face_det_onnx_path']):
                self.logger.debug(f"Initializing face detector with model: {self.config_data['cat_face_det_onnx_path']}")
                self.face_detector = Yolov5SCatFaceDetector(
                    self.config_data['cat_face_det_onnx_path'],
                    conf_thres=self.config_data['face_conf_thres'],
                    iou_thres=self.config_data['face_iou_thres']
                )
                self.logger.debug("Face detector initialized successfully")
                
            # 初始化猫脸识别器
            if self.config_data['cat_face_recog_onnx_path'] and os.path.exists(self.config_data['cat_face_recog_onnx_path']):
                self.logger.debug(f"Initializing face recognizer with model: {self.config_data['cat_face_recog_onnx_path']}")
                self.face_recognizer = EfficientNetV2B0_128_ONNX(
                    self.config_data['cat_face_recog_onnx_path']
                )
                self.logger.debug("Face recognizer initialized successfully")


        except Exception as e:
            self.logger.error(f"Failed to initialize detectors: {str(e)}", exc_info=True)
            QMessageBox.critical(self, "Error", f"Failed to initialize detectors: {str(e)}")

    def compute_register_face_embedding(self):
        self.logger.debug("============注册猫识别============")
        # 遍历所有注册的图片路径
        for i, imgpath in enumerate(self.register_imgpath_list, start=1):
            if i == 1:
                img_show = self.label_register1
            elif i == 2:
                img_show = self.label_register2
            elif i == 3:
                img_show = self.label_register3
            
            # 读取并转换图片   
            img = cv2.imread(imgpath)
            cv2.imwrite(f"./temple_img/orig_{i}.png", img)
            if img is None:
                self.logger.error(f"Failed to load image: {imgpath}")
                continue
            
            cat_det = self.cat_detector.detect(img)
            cat_num = len(cat_det)
            if cat_num != 1:
                raise ValueError("注册图片中有多只猫，请重新选择！")
            cat_ret = cat_det[0]
            x1, y1, x2, y2 = map(int, cat_ret[:4])
            conf = cat_ret[4]
            cat_img = img[y1:y2, x1:x2]
            self.logger.debug(f"注册的猫{i}检测, 置信度为：{conf:.2f}")
            cv2.imwrite(f"./temple_img/cat_{i}.png", cat_img)

            self.face_detector.set_threshold(0.6,0.6)
            face_det = self.face_detector.detect(cat_img)
            face_num = len(face_det)
            if face_num != 1:
                raise ValueError("注册图片中有多只猫脸，请重新选择！")            
            face_ret = face_det[0]
            x1, y1, x2, y2 = map(int, face_ret[:4])
            conf = face_ret[4]
            face_img = cat_img[y1:y2, x1:x2]   
            self.logger.debug(f"注册的猫脸{i}检测, 置信度为：{conf:.2f}")
            cv2.imwrite(f"./temple_img/face_{i}.png", face_img)     
            emb = self.face_recognizer.extract_features(img=face_img)
            self.register_embeddings_list.append(emb)
                    
            # Convert BGR to RGB for display
            img_rgb = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB)
            
            # Convert to QImage and display
            h, w, ch = img_rgb.shape
            bytes_per_line = ch * w
            q_img = QtGui.QImage(img_rgb.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
            pixmap = QtGui.QPixmap.fromImage(q_img)
            
            # Scale the pixmap to fit the label while maintaining aspect ratio
            scaled_pixmap = pixmap.scaled(
                img_show.size(),
                Qt.KeepAspectRatio, 
                Qt.SmoothTransformation
            )
            img_show.setPixmap(scaled_pixmap)
            self.logger.debug("register detection completed successfully")   
        self.logger.debug("============完成注册猫识别============")     

    def start_video(self):
        """开始或继续播放视频"""
        if not self.video_capture:
            self.video_capture = cv2.VideoCapture(self.config_data['video_path'])
            if not self.video_capture.isOpened():
                QMessageBox.warning(self, "错误", "无法打开视频文件！")
                return
            
            # 获取视频总帧数和帧率
            self.total_frames = int(self.video_capture.get(cv2.CAP_PROP_FRAME_COUNT))
            self.fps = self.video_capture.get(cv2.CAP_PROP_FPS)

        # 设置从当前帧开始播放
        self.video_capture.set(cv2.CAP_PROP_POS_FRAMES, self.current_frame_pos)

        # Convert FPS to milliseconds (1000ms / fps)
        self.interval = int(1000 / self.fps) 
        self.video_timer.start(self.interval)
        
        self.is_video_playing = True
        self.btn_start.setText("pause")
        self.logger.info(f"play: {self.config_data['video_path']}")

    def pause_video(self):
        """暂停视频播放"""
        self.video_timer.stop()
        self.is_video_playing = False
        self.btn_start.setText("start")
        self.logger.info("pause")

    def stop_video(self):
        """停止视频播放并释放资源"""
        self.video_timer.stop()
        if self.video_capture:
            self.video_capture.release()
            self.video_capture = None
        self.is_video_playing = False
        self.current_frame_pos = 0
        self.btn_start.setText("start")
        self.logger.info("stop")


    def run(self):
        """根据选择的输入类型调用对应的识别函数"""
        if self.config_data['img_path']:
            self.run_image()
        elif self.config_data['video_path']:
            if not self.is_video_playing:
                self.start_video()
            else:
                self.pause_video()
        elif self.usb_camera is not None:  # 添加USB相机判断
            if not self.is_usb_camera_running:
                self.start_usb_camera()
            else:
                self.pause_usb_camera()
        elif self.config_data['folder_path']:
            # 可以添加文件夹批量处理逻辑
            pass
        else:
            QMessageBox.warning(self, "警告", "请先选择输入源（图片/视频/USB相机/文件夹）")


    def closeEvent(self, event):
        """窗口关闭时释放资源"""
        self.stop_video()
        super().closeEvent(event)

    def run_cat_detection(self):
        self.logger.debug(f"img size: {self.camera_img.shape}") 
        try:
            # 初始化帧历史记录和状态
            if not hasattr(self, 'detection_history'):
                self.detection_history = []
                self.cat_present = False
                self.detection_started = False
                self.appearance_history = []
                self.frame_count = 0
                self.fps = 0
                self.prev_time = time.time()
                
            # 计算FPS
            self.frame_count += 1
            current_time = time.time()
            elapsed = current_time - self.prev_time
            
            if elapsed > 1.0:
                self.fps = self.frame_count / elapsed
                self.frame_count = 0
                self.prev_time = current_time
                
            if self.camera_img is None:
                self.logger.error("Failed to load image")
                QtWidgets.QMessageBox.warning(self, "Error", "Failed to load image!")
                return
            
            # Run detection
            detections = self.cat_detector.detect(self.camera_img)

            # 检测结果处理
            current_detected = False
            valid_detections = []
            
            if detections is not None:
                # 转换检测结果为统一格式
                if isinstance(detections, torch.Tensor):
                    detections = detections.cpu().numpy()
                
                # 处理多个检测结果
                if len(detections) > 0:
                    current_detected = True
                    valid_detections = detections
            
            # 猫出现/离开检测逻辑
            if not self.cat_present:
                self.appearance_history.append(current_detected)
                if len(self.appearance_history) > self.appearance_window:
                    self.appearance_history.pop(0)
                
                if len(self.appearance_history) == self.appearance_window:
                    detection_count = self.appearance_history.count(True)
                    if detection_count >= self.appearance_threshold:
                        self.cat_present = True
                        self.logger.info("检测到猫出现了!")
                        self.detection_history = []
                        self.appearance_history = []
            
            if self.cat_present:
                self.detection_history.append(current_detected)
                if len(self.detection_history) > self.detection_window_size:
                    self.detection_history.pop(0)
                
                if len(self.detection_history) == self.detection_window_size:
                    no_detection_count = self.detection_history.count(False)
                    if no_detection_count >= self.leave_threshold:
                        self.logger.info("所有猫都离开视野了!")
                        self.logger.info("----------------")
                        self.cat_present = False
                        self.detection_history = []
                        self.appearance_history = []
                        self.label_recog_cat1.clear()
                        self.label_recog_cat2.clear()
                        self.label_recog_cat3.clear()
                        self.current_result = 10000

            # 处理并显示所有检测到的猫
            if current_detected:
                self.logger.debug(f"Found {len(valid_detections)} cat detections")
                
                # 初始化输出图像数组
                self.cat_detector_output_img = []
                
                # 处理每个检测结果
                for i, det in enumerate(valid_detections):
                    # 获取检测框坐标
                    x1, y1, x2, y2 = map(int, det[:4])
                    conf = det[4] if len(det) > 4 else 0.0
                    
                    # 绘制检测框和标签
                    color = (0, 255, 0)  # 绿色
                    cv2.rectangle(self.camera_img, (x1, y1), (x2, y2), color, 2)
                    label = f"Cat {i+1}: {conf:.2f}"
                    cv2.putText(self.camera_img, label, (x1, y1-10), 
                                cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
                    
                    # 获取猫区域
                    cat_region = self.camera_img[y1:y2, x1:x2]
                    img_h, img_w = self.camera_img.shape[:2]
                    target_width, target_height = 640, 480
                    
                    # 计算原始位置比例
                    left_space = x1
                    right_space = img_w - x2
                    top_space = y1
                    bottom_space = img_h - y2
                    
                    # 计算需要添加的padding
                    pad_w_total = max(0, target_width - (x2 - x1))
                    pad_h_total = max(0, target_height - (y2 - y1))
                    
                    # 智能分配padding（保持原始位置比例）
                    pad_left = int(pad_w_total * (left_space / (left_space + right_space)))
                    pad_right = pad_w_total - pad_left
                    
                    pad_top = int(pad_h_total * (top_space / (top_space + bottom_space)))
                    pad_bottom = pad_h_total - pad_top
                    
                    # 添加padding
                    padded_cat = cv2.copyMakeBorder(
                        cat_region,
                        pad_top, pad_bottom,
                        pad_left, pad_right,
                        cv2.BORDER_CONSTANT,
                        value=(0, 0, 0)  # 黑色背景
                    )
                    
                    # 确保精确尺寸
                    padded_cat = cv2.resize(padded_cat, (target_width, target_height))
                    
                    # 将处理后的猫图像添加到数组中
                    self.cat_detector_output_img.append(padded_cat)
            
            # 显示FPS
            cv2.putText(self.camera_img, f"FPS: {self.fps:.1f}", (10, 30),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)

        except Exception as e:
            self.logger.error(f"Detection error: {str(e)}", exc_info=True)
            QtWidgets.QMessageBox.critical(self, "Error", f"Detection failed: {str(e)}")


    def run_cat_face_detection(self, img_list):
        try:
            self.logger.debug(f"Running cat face detection on {len(img_list)} cat images")
            
            # 初始化输出数组 - 只保存脸部区域图像
            self.face_detector_output_img = []
            
            # 处理每个猫图像
            for img_idx, img in enumerate(img_list):
                detections = self.face_detector.detect(img)
                
                if detections is not None:
                    for det in detections:
                        x1, y1, x2, y2 = map(int, det[:4])
                        conf = det[4]
                        cls_id = int(det[5])

                        # 1. 保存脸部区域图像
                        face_region = img[y1:y2, x1:x2]
                        self.face_detector_output_img.append(face_region)
                        
                        # 2. 在可视化图像上绘制边界框和标签（不影响保存的脸部区域）
                        color = (255, 255, 0)  # 黄色
                        thickness = 2
                        cv2.rectangle(self.camera_img, (x1, y1), (x2, y2), color, thickness)
                        label = f"face: {conf:.2f}"
                        cv2.putText(self.camera_img, label, (x2, y2 - 10), 
                                cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, thickness)
                        
                        self.logger.debug(f"Cat {img_idx+1} - Face: {conf:.2f} at ({x1},{y1})-({x2},{y2})")
                
            
            self.logger.debug(f"Detected {len(self.face_detector_output_img)} faces in total")
            
        except Exception as e:
            self.logger.error(f"Face detection failed: {str(e)}", exc_info=True)
            QtWidgets.QMessageBox.critical(self, "Error", f"Face detection error: {str(e)}")
            self.face_detector_output_img = []

    def run_cat_face_recognition(self, img_list):
        img = img_list[0]
        # 检查是否有注册的特征向量
        if not self.register_embeddings_list:
            self.logger.warning("No registered embeddings found for recognition")
            QMessageBox.warning(self, "Warning", "请先注册至少一只猫的脸!")
            return
            
        # 检查是否有有效的识别模型
        if not self.face_recognizer:
            self.logger.error("Face recognizer not initialized")
            QMessageBox.critical(self, "Error", "人脸识别器未初始化!")
            return
            
        # 初始化帧缓存和可配置参数
        if not hasattr(self, 'frame_history'):
            self.frame_history = []
            self.current_result = None  # 初始状态设置为无猫

            # 初始清除显示的猫图片
            self.label_recog_cat1.clear()
            self.label_recog_cat2.clear()
            self.label_recog_cat3.clear()
        
        # 提取当前图片的特征向量
        current_emb = self.face_recognizer.extract_features(img)
        
        # 计算与所有注册特征的距离
        distances = [np.linalg.norm(current_emb - emb) for emb in self.register_embeddings_list]
        min_dist = min(distances)
        best_match_idx = distances.index(min_dist)
        
        # 获取阈值
        threshold = self.config_data['face_dist_thres']
        
        # 确定当前帧的结果
        if min_dist > threshold:
            current_frame_result = -1  # 未知猫
        else:
            current_frame_result = best_match_idx 
        
        # 添加到历史记录(最多保留recognition_window_size帧)
        self.frame_history.append(current_frame_result)
        if len(self.frame_history) > self.recognition_window_size:
            self.frame_history.pop(0)
        
        # 只有收集到足够帧数时才进行判断
        if len(self.frame_history) == self.recognition_window_size:
            # 统计各类结果出现的次数
            cat1_count = self.frame_history.count(0)
            cat2_count = self.frame_history.count(1)
            cat3_count = self.frame_history.count(2)
            unknown_count = self.frame_history.count(-1)
            
            # 判断是否有足够数量的某种结果
            if cat1_count >= self.recognition_threshold:
                if self.current_result != 0:
                    self.current_result = 0
                    self.logger.info(f"识别到猫1")
                    # 显示猫1的图片
                    img_path = "temple_img/face_1.png"
                    img = cv2.imread(img_path)
                    if img is not None:
                        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                        h, w, ch = img_rgb.shape
                        bytes_per_line = ch * w
                        q_img = QtGui.QImage(img_rgb.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
                        pixmap = QtGui.QPixmap.fromImage(q_img)
                        scaled_pixmap = pixmap.scaled(self.label_recog_cat1.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
                        self.label_recog_cat1.setPixmap(scaled_pixmap)
                        # 清除另一只猫的显示
                        self.label_recog_cat2.clear()
                        self.label_recog_cat3.clear()
            
            elif cat2_count >= self.recognition_threshold:
                if self.current_result != 1:
                    self.current_result = 1
                    self.logger.info(f"识别到猫2")
                    # 显示猫2的图片
                    img_path = "temple_img/face_2.png"
                    img = cv2.imread(img_path)
                    if img is not None:
                        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                        h, w, ch = img_rgb.shape
                        bytes_per_line = ch * w
                        q_img = QtGui.QImage(img_rgb.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
                        pixmap = QtGui.QPixmap.fromImage(q_img)
                        scaled_pixmap = pixmap.scaled(self.label_recog_cat2.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
                        self.label_recog_cat2.setPixmap(scaled_pixmap)
                        # 清除另一只猫的显示
                        self.label_recog_cat1.clear()
                        self.label_recog_cat3.clear()

            elif cat3_count >= self.recognition_threshold:
                if self.current_result != 2:
                    self.current_result = 2
                    self.logger.info(f"识别到猫3")
                    img_path = "temple_img/face_3.png"
                    img = cv2.imread(img_path)
                    if img is not None:
                        img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                        h, w, ch = img_rgb.shape
                        bytes_per_line = ch * w
                        q_img = QtGui.QImage(img_rgb.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
                        pixmap = QtGui.QPixmap.fromImage(q_img)
                        scaled_pixmap = pixmap.scaled(self.label_recog_cat3.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
                        self.label_recog_cat3.setPixmap(scaled_pixmap)
                        # 清除另一只猫的显示
                        self.label_recog_cat1.clear()
                        self.label_recog_cat2.clear()

            elif unknown_count >= self.recognition_threshold:
                if self.current_result != -1:
                    self.current_result = -1
                    self.logger.info(f"识别到不在注册列表中的猫~~~")
                    # 清除所有猫的显示
                    self.label_recog_cat1.clear()
                    self.label_recog_cat2.clear()
                    self.label_recog_cat3.clear()
            self.frame_history = []


    def select_usb_camera(self):
        """选择USB相机（互斥）"""
        self.clear_selections()  # 清空其他选择
        
        try:
            # 先释放已存在的摄像头
            if self.usb_camera is not None:
                self.usb_camera.release()
                self.usb_camera = None
            
            # 尝试打开默认摄像头
            self.logger.debug("尝试打开USB摄像头...")
            self.usb_camera = cv2.VideoCapture(0, cv2.CAP_V4L2)  # 明确指定使用V4L2驱动
            
            # 验证摄像头是否成功打开
            if not self.usb_camera.isOpened():
                self.logger.error("无法打开USB摄像头")
                self.usb_camera = None
                self.btn_selectVideo_2.setEnabled(True)  # 确保按钮可点击
                QMessageBox.warning(self, "错误", "无法打开USB摄像头！")
                return False
            
            # 测试是否能读取一帧
            ret, frame = self.usb_camera.read()
 
            if not ret:
                self.logger.error("摄像头无法读取帧数据")
                self.usb_camera.release()
                self.usb_camera = None
                self.btn_selectVideo_2.setEnabled(True)  # 确保按钮可点击
                QMessageBox.warning(self, "错误", "摄像头无法正常工作！")
                return False
            
            # 清空其他路径选择
            self.path_selectImg.clear()
            self.path_selectVideo.clear()
            self.path_selectFolder.clear()
            
            # 更新配置数据
            self.config_data['img_path'] = ''
            self.config_data['video_path'] = ''
            self.config_data['folder_path'] = ''
            self.update_config_data()
            
            # 更新按钮状态
            self.btn_selectVideo_2.setEnabled(False)  # 摄像头在线时按钮变灰
            self.btn_selectVideo_2.setText("Camera Online")
            
            self.logger.info("USB摄像头已成功初始化")
            self.btn_start.setEnabled(True)
            return True
            
        except Exception as e:
            self.logger.error(f"摄像头初始化异常: {str(e)}")
            if self.usb_camera is not None:
                self.usb_camera.release()
                self.usb_camera = None
            self.btn_selectVideo_2.setEnabled(True)  # 异常时恢复按钮状态
            QMessageBox.warning(self, "错误", f"摄像头初始化失败: {str(e)}")
            return False
            
        except Exception as e:
            self.logger.error(f"摄像头初始化异常: {str(e)}")
            if self.usb_camera is not None:
                self.usb_camera.release()
                self.usb_camera = None
            QMessageBox.warning(self, "错误", f"摄像头初始化失败: {str(e)}")
            return False


    def start_usb_camera(self):
        """开始USB相机检测"""
        if self.usb_camera is None:
            if not self.select_usb_camera():  # 尝试自动初始化
                return
        try:
            # 确保摄像头仍然有效
            if not self.usb_camera.isOpened():
                self.logger.warning("摄像头连接已断开，尝试重新连接...")
                if not self.select_usb_camera():
                    return
            
            self.usb_camera_timer.start(30)  # 约30ms一帧
            self.is_usb_camera_running = True
            self.btn_start.setText("暂停")
            self.logger.info("USB摄像头检测已开始")
            
        except Exception as e:
            self.logger.error(f"启动摄像头失败: {str(e)}")
            self.stop_usb_camera()

    def pause_usb_camera(self):
        """暂停USB相机检测"""
        self.usb_camera_timer.stop()
        self.is_usb_camera_running = False
        self.btn_start.setText("start")
        self.logger.info("USB摄像头检测已暂停")

    def stop_usb_camera(self):
        """停止USB相机"""
        if self.usb_camera_timer.isActive():
            self.usb_camera_timer.stop()
        
        if self.usb_camera is not None:
            try:
                self.usb_camera.release()
            except Exception as e:
                self.logger.error(f"释放摄像头资源时出错: {str(e)}")
            finally:
                self.usb_camera = None
        
        # 恢复按钮状态
        self.btn_selectVideo_2.setEnabled(True)
        self.btn_selectVideo_2.setText("Camera Offline")
        
        self.is_usb_camera_running = False
        self.btn_start.setText("开始")
        self.logger.info("USB摄像头已停止")


    def clear_selections(self):
        """清空所有路径选择和图片显示"""
        # 如果USB相机正在运行，先停止
        if self.is_usb_camera_running:
            self.stop_usb_camera()
        
        # 如果视频正在播放，先停止
        if self.is_video_playing:
            self.stop_video()
            
        self.path_selectImg.clear()
        self.path_selectVideo.clear()
        self.path_selectFolder.clear()
        self.img_show_label.clear()
        self.img_show_label.setText("图片显示区域")
        self.btn_start.setText("开始")  # 确保按钮文本重置


    def run_video(self):
        if not self.detectors_initialized:
            self.update_config_data()  # 确保数据是最新的
                # 使用日志系统记录
            self.logger.debug("当前配置参数:")
            for key, value in self.config_data.items():
                self.logger.debug(f"{key}: {value}")

            self.logger.debug("Starting detection process...")
            self.initialize_detectors()
            self.detectors_initialized = True

        # 检查所有必要的检测器是否已初始化
        if not (hasattr(self, 'cat_detector') and hasattr(self, 'face_detector') and hasattr(self, 'face_recognizer')):
            self.logger.error("One or more detectors not initialized")
            QMessageBox.warning(self, "Error", "Please initialize all required detectors first!")
            return
        
        # 只在第一次运行时计算注册特征
        if not self.embeddings_computed:
            self.compute_register_face_embedding()
            self.embeddings_computed = True

        """处理视频帧"""
        if not self.video_capture:
            return
        
        ret, frame = self.video_capture.read()
        if not ret:
            self.stop_video()
            return
        
        # 更新当前帧位置
        self.current_frame_pos = int(self.video_capture.get(cv2.CAP_PROP_POS_FRAMES))
        
        if self.config_data['cat_det_enabled']:
            self.run_cat_detection()
        if self.config_data['face_det_enabled'] and self.cat_detector_output_img is not None:
            self.run_cat_face_detection(img=self.cat_detector_output_img )
        if self.config_data['face_recog_enabled'] and self.face_detector_output_img is not None:
            self.run_cat_face_recognition(img=self.face_detector_output_img)
        
        # 显示处理后的帧
        img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        h, w, ch = img_rgb.shape
        bytes_per_line = ch * w
        q_img = QtGui.QImage(img_rgb.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
        pixmap = QtGui.QPixmap.fromImage(q_img)
        scaled_pixmap = pixmap.scaled(
            self.img_show_label.size(),
            Qt.KeepAspectRatio, 
            Qt.SmoothTransformation
        )
        self.img_show_label.setPixmap(scaled_pixmap)
    
        self.cat_detector_output_img = []
        self.face_detector_output_img = []

    def run_image(self):
        """Run the detection on the selected image"""
        self.update_config_data()  # 确保数据是最新的
            # 使用日志系统记录
        self.logger.debug("当前配置参数:")
        for key, value in self.config_data.items():
            self.logger.debug(f"{key}: {value}")

        self.logger.debug("Starting detection process...")
        if not self.detectors_initialized:
            self.initialize_detectors()
            self.detectors_initialized = True
        # 检查所有必要的检测器是否已初始化
        if not (hasattr(self, 'cat_detector') and hasattr(self, 'face_detector') and hasattr(self, 'face_recognizer')):
            self.logger.error("One or more detectors not initialized")
            QMessageBox.warning(self, "Error", "Please initialize all required detectors first!")
            return
        
        # 只在第一次运行时计算注册特征
        if not self.embeddings_computed:
            self.compute_register_face_embedding()
            self.embeddings_computed = True

        self.logger.info("============实时猫识别============")
        if self.config_data['cat_det_enabled']:
            img = cv2.imread(self.config_data['img_path'])
            self.run_cat_detection(img=img)
        if self.config_data['face_det_enabled'] and self.cat_detector_output_img is not None:
            self.run_cat_face_detection(self.cat_detector_output_img)
        if self.config_data['face_recog_enabled'] and self.face_detector_output_img is not None:
            self.run_cat_face_recognition(self.face_detector_output_img)

        self.cat_detector_output_img = []
        self.face_detector_output_img = []


    def run_usb_camera(self):
        if not self.detectors_initialized:
            self.update_config_data()  # 确保数据是最新的
                # 使用日志系统记录
            self.logger.debug("当前配置参数:")
            for key, value in self.config_data.items():
                self.logger.debug(f"{key}: {value}")

            self.logger.debug("Starting detection process...")
            self.initialize_detectors()
            self.detectors_initialized = True

        # 检查所有必要的检测器是否已初始化
        if not (hasattr(self, 'cat_detector') and hasattr(self, 'face_detector') and hasattr(self, 'face_recognizer')):
            self.logger.error("One or more detectors not initialized")
            QMessageBox.warning(self, "Error", "Please initialize all required detectors first!")
            return
        
        # 只在第一次运行时计算注册特征
        if not self.embeddings_computed:
            self.compute_register_face_embedding()
            self.embeddings_computed = True

        """处理USB相机帧"""
        ret, frame = self.usb_camera.read()
        self.camera_img = frame.copy()  
        if not ret:
            self.stop_usb_camera()
            return
        
        # 复用视频处理逻辑
        if self.config_data['cat_det_enabled']:
            self.run_cat_detection()
        if self.config_data['face_det_enabled'] and len(self.cat_detector_output_img)>=1:
            self.run_cat_face_detection(self.cat_detector_output_img)
            self.cat_detector_output_img = []
        if self.config_data['face_recog_enabled'] and len(self.face_detector_output_img)>=1:
            self.run_cat_face_recognition(self.face_detector_output_img)
            self.face_detector_output_img = []
        
        # 显示处理后的帧
        img_rgb = cv2.cvtColor(self.camera_img, cv2.COLOR_BGR2RGB)
        h, w, ch = img_rgb.shape
        bytes_per_line = ch * w
        q_img = QtGui.QImage(img_rgb.data, w, h, bytes_per_line, QtGui.QImage.Format_RGB888)
        pixmap = QtGui.QPixmap.fromImage(q_img)
        scaled_pixmap = pixmap.scaled(
            self.img_show_label.size(),
            Qt.KeepAspectRatio, 
            Qt.SmoothTransformation
        )
        self.img_show_label.setPixmap(scaled_pixmap)
        
        

def sigint_handler(*args):
    """Handler for the SIGINT signal (Ctrl+C)."""
    QApplication.quit()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    # Set up signal handler for Ctrl+C
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    window = MyWindow()
    window.setWindowTitle("猫脸识别系统")
    window.show()
    sys.exit(app.exec_())